
#include "RtBase.h"
#include "RtHttpAuthInfoGetter.h"
#include "RtHttpAuthDlgResource.h"
#include "RtObserver.h"
#include "RtReactorInterface.h"

#ifdef RT_WIN32

#ifndef RT_USE_REACTOR_SELECT
  #define RT_ENABLE_DIALOG_IN_NETWORK_THREAD
#endif // !RT_USE_REACTOR_SELECT

#define RT_USE_ATRES

#ifdef _USRDLL
	extern HANDLE g_hInstDll;
#endif // _USRDLL

CRtAuthInfoGetterWin32DialogRequestEvent::
CRtAuthInfoGetterWin32DialogRequestEvent(const CRtString &aShow, 
										 int aType, 
										 IRtObserver *aObserver, 
										 IRtEventQueue *aEventQueue)
	: m_strShow(aShow)
	, m_nType(aType)
	, m_pObserver(aObserver)
	, m_pEventQueue(aEventQueue)
{
}

CRtAuthInfoGetterWin32DialogRequestEvent::~CRtAuthInfoGetterWin32DialogRequestEvent()
{
}

RtResult CRtAuthInfoGetterWin32DialogRequestEvent::OnEventFire()
{
	CRtString strUserName, strPassword;
	IRtHttpAuthInfoGetter::GETTER_RESULT rv = CRtHttpAuthInfoGetterWin32::Instance()->
		DoGetAuthInfo_i(m_strShow, m_nType, strUserName, strPassword);

	if (m_pEventQueue && rv != IRtHttpAuthInfoGetter::GETTER_WOULDBLOCK) {
		CRtAuthInfoGetterWin32DialogResponseEvent *pEvent = new 
			CRtAuthInfoGetterWin32DialogResponseEvent(m_pObserver, rv, m_pEventQueue);
		m_pEventQueue->PostEvent(pEvent);
	}
	
	return RT_OK;
}


CRtAuthInfoGetterWin32DialogResponseEvent::
CRtAuthInfoGetterWin32DialogResponseEvent(IRtObserver *aObserver, 
										  IRtHttpAuthInfoGetter::GETTER_RESULT aResult, 
										  IRtEventQueue *aEventQueue)
	: m_pObserver(aObserver)
	, m_Result(aResult)
	, m_pEventQueue(aEventQueue)
{
}

CRtAuthInfoGetterWin32DialogResponseEvent::~CRtAuthInfoGetterWin32DialogResponseEvent()
{
}

RtResult CRtAuthInfoGetterWin32DialogResponseEvent::OnEventFire()
{
	return  CRtHttpAuthInfoGetterWin32::Instance()->DoCallback_i(m_Result);
}

CRtHttpAuthInfoGetterWin32 *CRtHttpAuthInfoGetterWin32::Instance()
{
	return CRtSingletonT<CRtHttpAuthInfoGetterWin32>::Instance();
}
	
CRtHttpAuthInfoGetterWin32::CRtHttpAuthInfoGetterWin32()
	: m_nTypeIDD(0)
	, m_pDlgHwnd(NULL)
{
	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterWin32::CRtHttpAuthInfoGetterWin32");
}

CRtHttpAuthInfoGetterWin32::~CRtHttpAuthInfoGetterWin32()
{
}

IRtHttpAuthInfoGetter::GETTER_RESULT CRtHttpAuthInfoGetterWin32::
GetAuthInfoBlocked(const CRtString &aShow, int aType, 
				   CRtString &aUserName, CRtString &aPassword, 
				   IRtObserver *aObserver)
{
	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterWin32::GetAuthInfoBlocked,"
		" aShow=" << aShow << " aType=" << aType);
	
	if (aType == -1) {
		// it just get user name and password
		// we assume the user name shouldn't be empty if there is a proxy
		if (m_strUserName.empty())
			return GETTER_FAILURE;
		else {
			aUserName = m_strUserName;
			aPassword = m_strPassword;
			return GETTER_OK;
		}
	}

#ifdef RT_ENABLE_DIALOG_IN_NETWORK_THREAD
	IRtHttpAuthInfoGetter::GETTER_RESULT rv = 
		DoGetAuthInfo_i(aShow, aType, aUserName, aPassword);
	if (rv != GETTER_WOULDBLOCK) {
		IRtEventQueue* pEventQueueCurrent = CRtThreadManager::Instance()->
			GetThreadEventQueue(CRtThreadManager::TT_CURRENT);
		CRtAuthInfoGetterWin32DialogResponseEvent *pEvent = new 
			CRtAuthInfoGetterWin32DialogResponseEvent(aObserver, rv, pEventQueueCurrent);
		pEventQueueCurrent->PostEvent(pEvent);
		return rv;
	}
#endif // RT_ENABLE_DIALOG_IN_NETWORK_THREAD
	
	IRtEventQueue* pEventQueue = CRtThreadManager::Instance()->GetThreadEventQueue(CRtThreadManager::TT_CURRENT);
	{
		CRtMutexGuardT<MutexType> theGuard(m_Mutex);
		ObserversType::iterator iter = m_Observers.begin();
		for ( ; iter != m_Observers.end(); ++iter) {
			if ((*iter).first == aObserver) {
				RT_ASSERTE((*iter).second == pEventQueue);
				return GETTER_WOULDBLOCK;
			}
		}
		m_Observers.push_back(std::make_pair(aObserver, pEventQueue));
	}

#ifndef RT_ENABLE_DIALOG_IN_NETWORK_THREAD
	// the network is select() so that we have to borrow main thread to dispatch Windows messages.
	CRtAuthInfoGetterWin32DialogRequestEvent *pEvent = new 
		CRtAuthInfoGetterWin32DialogRequestEvent(aShow, aType, aObserver, pEventQueue);
	CRtThreadManager::Instance()->
		GetThreadEventQueue(CRtThreadManager::TT_MAIN)->PostEvent(pEvent);
#endif // !RT_ENABLE_DIALOG_IN_NETWORK_THREAD

//	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterWin32::GetAuthInfoBlocked,"
//		" m_strUserName=" << m_strUserName << 
//		" m_strPassword=" << m_strPassword);
//	aUserName = m_strUserName;
//	aPassword = m_strPassword;
	return GETTER_WOULDBLOCK;
}

RtResult CRtHttpAuthInfoGetterWin32::CancelResolve(IRtObserver *aObserver)
{
	CRtMutexGuardT<MutexType> theGuard(m_Mutex);
	ObserversType::iterator iter = m_Observers.begin();
	for ( ; iter != m_Observers.end(); ++iter) {
		if ((*iter).first == aObserver) {
			m_Observers.erase(iter);
			return RT_OK;
		}
	}
	return RT_ERROR_NOT_FOUND;
}

RtResult CRtHttpAuthInfoGetterWin32::
DoCallback_i(IRtHttpAuthInfoGetter::GETTER_RESULT aResult)
{
	IRtEventQueue* pEventQueue = CRtThreadManager::Instance()->GetThreadEventQueue(CRtThreadManager::TT_CURRENT);
	ObserversType observersTmp;
	{
		CRtMutexGuardT<MutexType> theGuard(m_Mutex);
		observersTmp.swap(m_Observers);
	}

	ObserversType::iterator iter = observersTmp.begin();
	for ( ; iter != observersTmp.end(); ++iter) {
		if ((*iter).second != pEventQueue) {
			RT_ERROR_TRACE_THIS("IRtHttpAuthInfoGetter::DoCallback_i,"
				" pEventQueue is not the same."
				" pEventQueue=" << pEventQueue << 
				" second=" << (*iter).second);
			continue;
		}
		CObserverBuffer bufTmp(aResult, m_strUserName, m_strPassword);
		(*iter).first->OnObserve("AuthInfoGetter", &bufTmp);
	}
	return RT_OK;
}

IRtHttpAuthInfoGetter::GETTER_RESULT CRtHttpAuthInfoGetterWin32::
DoGetAuthInfo_i(const CRtString &aShow, int aType, 
				CRtString &aUserName, CRtString &aPassword)
{
	HINSTANCE hInstance;
	BOOL bNeedFree = FALSE;
#if defined (_USRDLL)
	hInstance = static_cast<HINSTANCE>(g_hInstDll);
#else // !_USRDLL
	hInstance = ::GetModuleHandle(NULL);
#endif // _USRDLL

	if (m_pDlgHwnd) {
		RT_WARNING_TRACE_THIS("CRtHttpAuthInfoGetterWin32::DoGetAuthInfo_i,"
			" dialog is popuping, m_pDlgHwnd=" << m_pDlgHwnd);
		return GETTER_WOULDBLOCK;
	}

	if (aType == 0) {
		CRtString::size_type nFind = aShow.find('\n');
		if (nFind != CRtString::npos) {
			m_strHostAndPort = aShow.substr(0, nFind);
			m_strRealm = aShow.substr(nFind + 1);
		}
		else {
			m_strHostAndPort = aShow;
			m_strRealm.resize(0);
		}
		m_nTypeIDD = IDD_DIALOG_BASIC;
	}
	else {
		m_strHostAndPort = aShow;
		m_strRealm.resize(0);
		m_nTypeIDD = IDD_DIALOG_NTLM;
	}

	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterWin32::GetAuthInfoBlocked,"
		" popup dialog, hInstance=" << hInstance << 
		" bNeedFree=" << bNeedFree);

	int nDlg = ::DialogBoxParamA(
		hInstance, 
		MAKEINTRESOURCE(m_nTypeIDD), 
//		::GetActiveWindow(), 
		::GetDesktopWindow(), 
		(DLGPROC)CRtHttpAuthInfoGetterWin32::StartDialogProc, 
		(LPARAM)(void *)this);

	if (nDlg == -1) {
		RT_ERROR_TRACE("CRtHttpAuthInfoGetterWin32::GetAuthInfoBlocked," 
			" nDlg=" << nDlg << " err=" << ::GetLastError());
	}

	if (bNeedFree)
		::FreeLibrary(hInstance);

	m_pDlgHwnd = NULL;
	aUserName = m_strUserName;
	aPassword = m_strPassword;
	return nDlg == -1 ? GETTER_FAILURE : (IRtHttpAuthInfoGetter::GETTER_RESULT)nDlg;
}

LRESULT CRtHttpAuthInfoGetterWin32::
StartDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL bRet = TRUE;
	BOOL dwResult = 0;
	CRtHttpAuthInfoGetterWin32 *pGetter = CRtHttpAuthInfoGetterWin32::Instance();
	RT_ASSERTE(pGetter);
	
	switch(uMsg) {
	case WM_INITDIALOG:
		RT_ASSERTE(pGetter == (CRtHttpAuthInfoGetterWin32 *)lParam);
		::SetDlgItemTextA(hWnd, IDC_STATIC_FIREWALL, pGetter->m_strHostAndPort.c_str());
		if (pGetter->m_nTypeIDD != IDD_DIALOG_NTLM)
			::SetDlgItemTextA(hWnd, IDC_STATIC_REALM, pGetter->m_strRealm.c_str());
		((CRtHttpAuthInfoGetterWin32 *)lParam)->m_pDlgHwnd = hWnd;
		::SetFocus(::GetDlgItem(hWnd, IDC_EDIT_USERNAME));
		dwResult = FALSE;

		::ShowWindow(hWnd, SW_HIDE);
		::ShowWindow(hWnd, SW_SHOWNORMAL);

		break;
	case WM_CLOSE:
		::EndDialog(hWnd, GETTER_CANCEL);
		break;
	case WM_COMMAND: 
		switch (LOWORD(wParam)) {
		case IDOK: {
			char szBuf[1024];
			UINT uLen;
			uLen = ::GetDlgItemTextA(hWnd, IDC_EDIT_USERNAME, szBuf, sizeof(szBuf));
			if (uLen == 0) 
				return TRUE;
			pGetter->m_strUserName.assign(szBuf, uLen);
			
			if (pGetter->m_nTypeIDD == IDD_DIALOG_NTLM) {
				uLen = GetDlgItemTextA(hWnd, IDC_EDIT_DOMAIN, szBuf, sizeof(szBuf));
				if (uLen) {
					CRtString strDomainAndUser(szBuf, uLen);
					pGetter->m_strUserName = strDomainAndUser + "\\" + pGetter->m_strUserName;
				}
			}
			
			uLen = ::GetDlgItemTextA(hWnd, IDC_EDIT_PASSWORD, szBuf, sizeof(szBuf));
			pGetter->m_strPassword.assign(szBuf, uLen);
			::EndDialog(hWnd, GETTER_OK);
			break;
				   }
		case IDCANCEL:
			::EndDialog(hWnd, GETTER_CANCEL);
			break;
		default:
			bRet = FALSE;
			break;
		}
		break;

	default:
		bRet = FALSE;
		break;
	}

	if (bRet) {
		switch (uMsg) {
		case WM_COMPAREITEM:
		case WM_VKEYTOITEM:
		case WM_CHARTOITEM:
		case WM_INITDIALOG:
		case WM_QUERYDRAGICON:
		case WM_CTLCOLORMSGBOX:
		case WM_CTLCOLOREDIT:
		case WM_CTLCOLORLISTBOX:
		case WM_CTLCOLORBTN:
		case WM_CTLCOLORDLG:
		case WM_CTLCOLORSCROLLBAR:
		case WM_CTLCOLORSTATIC:
			return dwResult;
			break;
		}
		::SetWindowLong(hWnd, DWL_MSGRESULT, dwResult);
		return TRUE;
	}
//	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
	return FALSE;
}

void CRtHttpAuthInfoGetterWin32::
InterruptGetAuthInfoBlocked(IRtObserver *aObserver)
{
	if (aObserver)
		CancelResolve(aObserver);
	
	if (m_pDlgHwnd) {
		RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterWin32::InterruptGetAuthInfoBlocked.");
		RT_ASSERTE(::IsWindow(m_pDlgHwnd));
		::EndDialog(m_pDlgHwnd, GETTER_INTEREPT);
		m_pDlgHwnd = NULL;

//		m_Event.Signal();
	}
}

#endif // RT_WIN32


class CAuthInfoGetterEvent : public IRtEvent
{
public:
	CAuthInfoGetterEvent(IRtObserver *aObserver, LPCSTR aTopic, 
						 const CRtString &aShow)
		: m_pObserver(aObserver)
		, m_pszTopic(aTopic)
		, m_strShow(aShow)
	{
	}

	virtual RtResult OnEventFire()
	{
		m_pObserver->OnObserve(
			m_pszTopic,
			const_cast<LPSTR>(m_strShow.c_str()));
		return RT_OK;
	}

private:
	IRtObserver *m_pObserver;
	LPCSTR m_pszTopic;
	CRtString m_strShow;
};


CRtHttpAuthInfoGetterByUpperLayer* CRtHttpAuthInfoGetterByUpperLayer::Instance()
{
	return CRtSingletonT<CRtHttpAuthInfoGetterByUpperLayer>::Instance();
}

CRtHttpAuthInfoGetterByUpperLayer::CRtHttpAuthInfoGetterByUpperLayer()
	: m_pObserver(NULL)
	, m_bNeedInterrupt(FALSE)
{
	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterByUpperLayer::CRtHttpAuthInfoGetterByUpperLayer");
}

CRtHttpAuthInfoGetterByUpperLayer::~CRtHttpAuthInfoGetterByUpperLayer()
{
}

IRtHttpAuthInfoGetter::GETTER_RESULT CRtHttpAuthInfoGetterByUpperLayer::
GetAuthInfoBlocked(const CRtString &aShow, int aType, 
				   CRtString &aUserName, CRtString &aPassword, 
				   IRtObserver *aObserver)
{
	RT_INFO_TRACE_THIS("CRtHttpAuthInfoGetterByUpperLayer::GetAuthInfoBlocked,"
		" aShow=" << aShow << " aType=" << aType);
	
	if (aType == -1) {
		// it just get user name and password
		// we assume the user name shouldn't be empty if there is a proxy
		if (m_strUserName.empty())
			return GETTER_CANCEL;
		else {
			aUserName = m_strUserName;
			aPassword = m_strPassword;
			return GETTER_OK;
		}
	}

	if (m_pObserver) {
		CRtThread *pMain = CRtThreadManager::Instance()->GetThread(CRtThreadManager::TT_MAIN);
		if (!CRtThreadManager::IsEqualCurrentThread(pMain->GetThreadId())) {
			CAuthInfoGetterEvent *pEvent = new 
				CAuthInfoGetterEvent(m_pObserver, "HttpAuthInfoGetterByUpperLayer_g", aShow);
			pMain->GetEventQueue()->PostEvent(pEvent);

			m_bNeedInterrupt = TRUE;
			m_Event.Wait();
			m_bNeedInterrupt = FALSE;
		}
	}
	
	aUserName = m_strUserName;
	aPassword = m_strPassword;
	return m_strUserName.empty() ? GETTER_CANCEL : GETTER_OK;
}

void CRtHttpAuthInfoGetterByUpperLayer::InterruptGetAuthInfoBlocked(IRtObserver *aObserver)
{
	if (m_bNeedInterrupt) {
		CAuthInfoGetterEvent *pEvent = new 
			CAuthInfoGetterEvent(m_pObserver, "HttpAuthInfoGetterByUpperLayer_i", CRtString());
		CRtThreadManager::Instance()->
			GetThreadEventQueue(CRtThreadManager::TT_MAIN)->PostEvent(pEvent);
		m_Event.Signal();
	}
}

void CRtHttpAuthInfoGetterByUpperLayer::RegisterNotifaction(IRtObserver *aObserver)
{
	m_pObserver = aObserver;
}

void CRtHttpAuthInfoGetterByUpperLayer::
SetUserNameAndPassword(const CRtString &aUserName, const CRtString &aPassword)
{
	m_strUserName = aUserName;
	m_strPassword = aPassword;
	m_Event.Signal();
}
